﻿namespace Seagull.EventBus
{
    internal class EventBus : IEventBus
    {
        private readonly Type[] typeCollection;

        public EventBus()
        {
            typeCollection = App.EffectiveTypes.Where(type => type.IsClass && type.GetInterfaces().Any(item => item.IsGenericType && item.GetGenericTypeDefinition() == typeof(IEventHandler<>))).ToArray();
        }

        public async Task<IEventBusResponse> Publish<TData>(TData data) where TData : IEvent, new()
        {

            var types = typeCollection.Where(type => type.GetInterfaces().Any(item => item == typeof(IEventHandler<TData>)));
            var instances = new List<IEventHandler<TData>>();
            foreach (var type in types)
            {
                // 从容器服务中提取，如果没有，则尝试自行构建对象。
                object? instance;
                Type serviceType = type;
                var serviceDescriptor = App._services.FirstOrDefault(x =>
                {
                    if (x.ServiceType == type) return true;
                    else if (x.ImplementationType == type)
                    {
                        serviceType = x.ServiceType;
                        return true;
                    }
                    else
                        return false;
                });
                switch (serviceDescriptor?.Lifetime)
                {
                    case ServiceLifetime.Singleton:
                        instance = App._provider.GetRequiredService(serviceType);
                        break;
                    case ServiceLifetime.Scoped:
                    case ServiceLifetime.Transient:
                        if (App.HttpContext is null)
                            throw new NullReferenceException("HttpContext is null.");
                        instance = ActivatorUtilities.GetServiceOrCreateInstance(App.HttpContext.RequestServices, type);
                        //instance = App.HttpContext.RequestServices.GetRequiredService(serviceType);
                        break;
                    default:
                        var ctor = type.GetConstructor(Array.Empty<Type>());
                        if (ctor is null) continue;
                        instance = ctor.Invoke(null);
                        break;
                }
                if (instance is IEventHandler<TData> eventHandler)
                {
                    instances.Add(eventHandler);
                }
            }

            List<object> consumers = new List<object>();
            foreach (var instance in instances)
            {
                var item = Internal.TransExp.Trans(data);
                EventContext<TData> context = new(item);
                await instance.Handle(context);
                consumers.Add(instance);
            }
            EventBusResponse response = new(consumers);
            return response;
        }
    }
}